﻿Option Strict On
'    Copyright (C) 2018-2022 Hazel Ward
' 
'    This file is a part of Winapp2ool
' 
'    Winapp2ool is free software: you can redistribute it and/or modify
'    it under the terms of the GNU General Public License as published by
'    the Free Software Foundation, either version 3 of the License, or
'    (at your option) any later version.
'
'    Winapp2ool is distributed in the hope that it will be useful,
'    but WITHOUT ANY WARRANTY; without even the implied warranty of
'    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
'    GNU General Public License for more details.
'
'    You should have received a copy of the GNU General Public License
'    along with Winapp2ool.  If not, see <http://www.gnu.org/licenses/>.
Imports System.Management.Instrumentation
''' <summary>
''' This module was designed with the help of Fred de Vries. It produces a winapp2.ini entry to remove irrelevant registry keys generated by the Java Runtime Environment installer. 
''' </summary>
Module JavaMaker

    ''' <summary>Indicates that the java.ini file should be downloaded from GitHub</summary>
    Private Property DownloadJavaDefs As Boolean = Not isOffline
    '''<summary>The winapp2.ini that may potentially be merged into</summary>
    Private Property JavaMakerFile1 As New iniFile(Environment.CurrentDirectory, "winapp2.ini")
    '''<summary>The local copy of the JRE definitions file</summary>
    Private Property JavaMakerFile2 As New iniFile(Environment.CurrentDirectory, "java.ini", mExist:=True)
    '''<summary>The default path for saving generated entries to disk</summary>
    Private Property JavaMakerFile3 As New iniFile(Environment.CurrentDirectory, "java-generated.ini")
    '''<summary>Indicates whether or not the JRE entry should be saved to disk</summary>
    Private Property SaveGeneratedEntry As Boolean = False
    '''<summary>Indicates whether or not the JRE entry should be merged into another file</summary>
    Private Property MergeGeneratedEntry As Boolean = False
    '''<summary>Indicates whether or not module settings have been modified</summary>
    Private Property ModuleSettingsChanged As Boolean = False
    ''' <summary>Restores the module parameters to their default states</summary>
    Private Sub initDefaultParams()
        DownloadJavaDefs = Not isOffline
        JavaMakerFile1.resetParams()
        JavaMakerFile2.resetParams()
        JavaMakerFile3.resetParams()
        SaveGeneratedEntry = False
        MergeGeneratedEntry = False
        ModuleSettingsChanged = False
    End Sub

    ''' <summary>Prints the JavaMaker main menu</summary>
    Public Sub printJMMenu()
        printMenuTop({"Creates a winapp2.ini entry that cleans up after old Java versions"})
        print(1, "Run (Default)", "Attempt to create an entry based on the current system", trailingBlank:=True)
        print(1, "Toggle Download", $"downloading of java.ini from GitHub", trailingBlank:=DownloadJavaDefs, enStrCond:=DownloadJavaDefs)
        print(1, "File Chooser (java.ini)", "Select a new local file path for java.ini", Not DownloadJavaDefs, trailingBlank:=True)
        print(5, "Toggle Merge", "merging the generated entry into winapp2.ini automatically", enStrCond:=MergeGeneratedEntry)
        print(5, "Toggle Save", "saving the generated entry to disk", Not MergeGeneratedEntry, trailingBlank:=Not SaveGeneratedEntry, enStrCond:=SaveGeneratedEntry)
        print(1, "File Chooser (save)", "Select where winapp2ool saves the generated entry", Not MergeGeneratedEntry And SaveGeneratedEntry, trailingBlank:=True)
        print(0, $"Java definitions file: {If(Not DownloadJavaDefs, replDir(JavaMakerFile2.Path), "online")}", closeMenu:=Not (SaveGeneratedEntry Or ModuleSettingsChanged))
        print(0, $"Save file: {replDir(JavaMakerFile3.Path)}", cond:=SaveGeneratedEntry, closeMenu:=Not ModuleSettingsChanged)
        print(2, "JavaMaker", cond:=ModuleSettingsChanged, closeMenu:=True)
    End Sub

    ''' <summary>Handles the JavaMaker menu input</summary>
    ''' <param name="input"></param>
    Public Sub handleJMInput(input As String)
        Select Case True
            Case input = "0"
                exitModule()
            Case input = "1" Or input.Length = 0
                makeSomeJava()
            Case input = "2"
                'toggleSettingParam(DownloadJavaDefs, "Downloading", ModuleSettingsChanged)
            Case input = "3" And Not DownloadJavaDefs
                'changeFileParams(JavaMakerFile2, ModuleSettingsChanged)
            Case input = "3" And DownloadJavaDefs Or input = "4" And Not DownloadJavaDefs
                'toggleSettingParam(SaveGeneratedEntry, "Saving", ModuleSettingsChanged)
            Case SaveGeneratedEntry And (input = "5" And Not DownloadJavaDefs) Or (input = "4" And DownloadJavaDefs)
                'changeFileParams(JavaMakerFile3, ModuleSettingsChanged)
            Case input = "6" And Not DownloadJavaDefs Or input = "5" And DownloadJavaDefs
                'toggleSettingParam(MergeGeneratedEntry, "Merging", ModuleSettingsChanged)
            Case ModuleSettingsChanged And
                ((input = "6" And Not DownloadJavaDefs And SaveGeneratedEntry) Or
                (input = "5" And Not (DownloadJavaDefs Xor SaveGeneratedEntry)) Or
                (input = "4" And Not DownloadJavaDefs And SaveGeneratedEntry))
                resetModuleSettings("JavaMaker", AddressOf initDefaultParams)
            Case Else
                setHeaderText(invInpStr, True)
        End Select
    End Sub

    ''' <summary>Generates the RegKeylist for the current system</summary>
    ''' <param name="kls">An array of keylists containing the RegKeys that will be in the generated entry</param>
    Private Function mkEntry(kls As keyList()) As keyList
        Dim out As New keyList
        For Each lst In kls
            lst.removeLast()
            out.add(lst.Keys)
        Next
        Return out
    End Function

    ''' <summary>Creates RegKeys conditionally based on their presence on the current system</summary>
    ''' <param name="reg">The Registry key to observe subkeys of</param>
    ''' <param name="searches">The strings to be searched for in the subkeys</param>
    Private Function getRegKeys(reg As Microsoft.Win32.RegistryKey, searches As List(Of String)) As keyList
        Dim out As New keyList
        Dim keyNum = 0
        Try
            Dim keys = reg.GetSubKeyNames
            For Each search In searches
                For Each key In keys
                    If key.Contains(search) Then
                        keyNum += 1
                        out.add(New iniKey($"RegKey{keyNum}={reg.ToString}\{key}"))
                    End If
                Next
            Next
        Catch ex As NullReferenceException
            ' The only Exception we can expect here is that reg is not set to an instance of an object
            ' This occurs when the requested registry key does not exist on the current system
            ' We can just silently fail if that's the case 
        End Try
        Return out
    End Function

    ''' <summary>Creates a winapp2.ini entry to clean up after the Java installation process</summary>
    Private Sub makeSomeJava()
        ' Load the java.ini file
        If DownloadJavaDefs Then JavaMakerFile2 = getRemoteIniFile(javaLink) Else If Not enforceFileHasContent(JavaMakerFile2) Then Exit Sub
        ' Get JavaPlugin and JavaScript keys in HKCR\
        Dim JavaPluginKeys = getRegKeys(Microsoft.Win32.Registry.ClassesRoot, {"JavaPlugin", "JavaScript"}.ToList)
        ' Get the CLSIDs present on the current system
        Dim clsids As New keyList("CLSID")
        Dim kll As New List(Of keyList) From {clsids, New keyList("Errors")}
        cwl("Running an intense registry query, this will take a few moments...")
        Dim jSect As iniSection = JavaMakerFile2.Sections.Item("Previous Java Installation Cleanup *")
        jSect.constKeyLists(kll)
        Dim IDS = clsids.toStrLst(True)
        Dim typeLib = getRegKeys(getCRKey("WOW6432Node\TypeLib\"), {"{5852F5E0-8BF4-11D4-A245-0080C6F74284}"}.ToList)
        Dim classRootIDs = getRegKeys(getCRKey("WOW6432Node\CLSID\"), IDS.Items)
        Dim localMachineClassesIDs = getRegKeys(getLMKey("SOFTWARE\Classes\WOW6432Node\CLSID\"), IDS.Items)
        Dim localMachineWOWIds = getRegKeys(getLMKey("SOFTWARE\WOW6432Node\Classes\CLSID\"), IDS.Items)
        Dim defClassesIDs = getRegKeys(getUserKey(".Default\Software\Classes\CLSID\"), IDS.Items)
        Dim s1518ClassesIDs = getRegKeys(getUserKey("S-1-5-18\Software\Classes\CLSID\"), IDS.Items)
        Dim localMachineJREs = getRegKeys(getLMKey("Software\JavaSoft\Java Runtime Environment"), {"1"}.ToList)
        ' Get the JRE versions from HKLM\ and HKCU\, ignore the most recent
        Dim lmJREminorIDs, cuJREminorIDs As New keyList
        ' Some keys have the format 1.W.0_XYZ, they match the main 1.W key that also exists along side them and should be separated out
        localMachineJREs.Keys.ForEach(Sub(key) lmJREminorIDs.add(key, key.toString.Replace("HKEY_LOCAL_MACHINE", "").Contains("_")))
        localMachineJREs.remove(lmJREminorIDs.Keys)
        Dim currentUserJREs = getRegKeys(getCUKey("Software\JavaSoft\Java Runtime Environment\"), {"1"}.ToList)
        currentUserJREs.Keys.ForEach(Sub(key) cuJREminorIDs.add(key, key.toString.Replace("HKEY_CURRENT_USER", "").Contains("_")))
        currentUserJREs.remove(cuJREminorIDs.Keys)
        ' Generate the list of RegKeys
        Dim regKeyList = mkEntry({classRootIDs, localMachineClassesIDs, localMachineWOWIds, defClassesIDs, s1518ClassesIDs,
                localMachineJREs, lmJREminorIDs, currentUserJREs, cuJREminorIDs, JavaPluginKeys})
        ' Renumber them 
        'regKeyList.renumberKeys(replaceAndSort(regKeyList.toStrLst(True), "-", "-"))
        ' Generate the new entry
        Dim entry As New List(Of String)
        clrConsole()
        entry.Add("[Java Installation Cleanup *]")
        entry.Add("Section=Experimental")
        entry.Add($"Detect={regKeyList.Keys(0).Value}")
        entry.Add("Default=False")
        Dim out As New iniSection(entry)
        regKeyList.Keys.ForEach(Sub(key) out.Keys.add(key))
        cwl(out.ToString)
        If SaveGeneratedEntry Then
            cwl() : cwl($"Saving {JavaMakerFile3.Name}")
            JavaMakerFile3.overwriteToFile(out.ToString)
            cwl("Save complete.")
        End If
        cwl(anyKeyStr)
        Console.ReadLine()
    End Sub
End Module